﻿using System;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Activities.Presentation.View;
using System.ComponentModel.Composition.Hosting;
using System.Collections.Generic;

namespace Vision.VisionWorkshop.Common
{
    /// <summary>
    /// 
    /// </summary>
    public static class IntillisenseService
    {
        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public static IExpressionEditorService BuildIntillisenseService()
        {            
            AssemblyCatalog assemblyCatalog = new AssemblyCatalog();
            assemblyCatalog.AssemblyResolveEvent += new AssemblyCatalog.AssemblyResolve(assemblyCatalog_AssemblyResolveEvent);

            assemblyCatalog.LoadAssembly("Microsoft.VisualBasic.Editor, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", false);
            assemblyCatalog.LoadAssembly("Microsoft.VisualStudio.Shell.10.0, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", false);
            assemblyCatalog.LoadAssembly("Microsoft.VisualStudio.Platform.VSEditor, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", false);
            assemblyCatalog.LoadAssembly("Microsoft.VisualStudio.Platform.VSEditor.Interop, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", false);
            assemblyCatalog.LoadAssembly("Microsoft.VisualStudio.Editor.Implementation, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", false);

            assemblyCatalog.LoadAssembly(@"C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\Addins\WorkflowDesigner\Microsoft.VisualStudio.Activities.Addin.dll", true);
            assemblyCatalog.LoadAssembly(@"C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\AddinViews\Microsoft.VisualStudio.Activities.AddinView.dll", true);
                                                            
            var compType = assemblyCatalog.GetType("Microsoft.VisualBasic.Editor.ExpressionEditor.HostableVBCompiler", true, false);
            var editType = assemblyCatalog.GetType("Microsoft.VisualBasic.Editor.HostableEditor", true, false);

            editType.GetMethod("set_GetHostableVBCompiler").Invoke(null, new object[] { Activator.CreateInstance(compType, new CompositionContainer(assemblyCatalog.GetAggregateCatalog())) });

            var eesType = assemblyCatalog["Microsoft.VisualStudio.Activities.AddIn, Version=10.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"].GetType("Microsoft.VisualStudio.Activities.AddIn.ExpressionEditorService");

            return (IExpressionEditorService)Activator.CreateInstance(eesType);

            Microsoft.VisualBasic.VBCodeProvider provider;
            
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        /// <returns></returns>
        private static Assembly assemblyCatalog_AssemblyResolveEvent(object sender, ResolveEventArgs args)
        {
            string path = string.Empty;
            string name = args.Name;            
            int idx = args.Name.IndexOf(',');
            
            if (idx > 0){
                name = args.Name.Substring(0, idx);
            }

            if (File.Exists(args.Name)){
                return Assembly.LoadFile(args.Name);
            }
            
            if (Environment.GetEnvironmentVariable("VS100COMNTOOLS") != null){
                string[] files = Directory.GetFiles(@"C:\Program Files (x86)\Microsoft Visual Studio 10.0", name + ".dll", SearchOption.AllDirectories);
                if (files.Length > 0){
                    return Assembly.LoadFile(files[0]);    
                }
            }

            return null;
        }        
    }

    /// <summary>
    /// 
    /// </summary>
    public class AssemblyCatalog : Dictionary<string, Assembly>
    {                
        public delegate Assembly AssemblyResolve(object sender, ResolveEventArgs args);
        public event AssemblyResolve AssemblyResolveEvent;
                   
        /// <summary>
        /// 
        /// </summary>
        public AssemblyCatalog(): base()
        {
            
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="assemblyRef"></param>
        /// <param name="includeReferencedAssemblies"></param>
        public void LoadAssembly(string assemblyRef, bool includeReferencedAssemblies)
        {
            if (this.AssemblyResolveEvent != null){
                AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
            }
            
            Assembly assembly = Assembly.Load(assemblyRef);
            if (assembly != null){
                if(!this.ContainsKey(assembly.FullName)){
                    this.Add(assembly.FullName, assembly);                    
                    foreach (AssemblyName refAssembly in assembly.GetReferencedAssemblies()){
                        if (!this.ContainsKey(refAssembly.FullName)){                            
                            this.Add(refAssembly.FullName, Assembly.Load(refAssembly.FullName));                                                
                        }                        
                    }    
                }                                                
            }

            if (this.AssemblyResolveEvent != null){
                AppDomain.CurrentDomain.AssemblyResolve -= new ResolveEventHandler(CurrentDomain_AssemblyResolve);
            }
        }
                        
        /// <summary>
        /// 
        /// </summary>
        /// <param name="name"></param>
        /// <param name="throwOnError"></param>
        /// <param name="ignoreCase"></param>
        /// <returns></returns>
        public Type GetType(string name, bool throwOnError, bool ignoreCase)
        {
            foreach (Assembly assembly in this.Values){
                Type type = assembly.GetType(name, throwOnError, ignoreCase);                                                
                if (type != null){
                    return type;                    
                }
            }
            return null;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public AggregateCatalog GetAggregateCatalog()
        {
            AggregateCatalog aggCatalog = new AggregateCatalog();
            foreach (Assembly assembly in this.Values){
                aggCatalog.Catalogs.Add(new System.ComponentModel.Composition.Hosting.AssemblyCatalog(assembly));
            }
            return aggCatalog;            
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        /// <returns></returns>
        private Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
        {
            return this.AssemblyResolveEvent(sender, args);
        }
    }
}


